#include "rtda/heap/cp_methodref.h"
#include "classfile/class_file.hpp"
#include "rtda/heap/method.h"
#include "rtda/heap/constant_pool.hpp"
#include "rtda/heap/method_lookup.h"

namespace jvm {
namespace heap {

MethodRef::MethodRef(std::shared_ptr<ConstantPool> pCp, const std::shared_ptr<classfile::ConstantMethodRefInfo> pMemberrefInfo) 
    : MemberRef(pCp) 
{
    copyMemberRefInfo(pMemberrefInfo);
}

std::shared_ptr<MethodRef> MethodRef::newMethodRef(std::shared_ptr<ConstantPool> pCp, 
    const std::shared_ptr<classfile::ConstantMethodRefInfo> pMethodrefInfo) {
    return std::make_shared<MethodRef>(pCp, pMethodrefInfo);
}

std::shared_ptr<Method> MethodRef::resolvedMethod() {
    if (!m_pMethod) {
        resolveMethodRef();
    }
    return m_pMethod;
}

void MethodRef::resolveMethodRef() {
    auto d = m_pCp->getClass();
    auto c = resolvedClass();

    if (c->isInterface()) {
        throw std::runtime_error("java.lang.IncompatibleClassChangeError");
    }

    auto method = lookupMethod(c, m_name, m_descriptor);
    if (!method) {
        throw std::runtime_error("java.lang.NoSuchMethodError");
    }
    if (!method->isAccessibleTo(d)) {
        throw std::runtime_error("java.lang.IllegalAccessError");
    }

    m_pMethod = method;
}

std::shared_ptr<Method> MethodRef::lookupMethod(std::shared_ptr<Class> pClass, const std::string& name, const std::string& descriptor)
{
    auto method = lookupMethodInClass(pClass, name, descriptor);
    if (!method) {
        method = lookupMethodInInterfaces(pClass->getInterfaces(), name, descriptor);
    }
    return method;
}

} // namespace heap
} // namespace jvm